using System.Collections.Generic;
using System.Windows.Forms;

namespace ShadeTree.Binding
{
    public class ScreenStateMachine<ENUM>
    {
        private readonly ControlSet _all = new ControlSet();
        private readonly IScreenBinder _binder;
        private readonly Dictionary<ENUM, IScreenState> _states = new Dictionary<ENUM, IScreenState>();
        private IScreenState _currentState = new NulloScreenState();

        public ScreenStateMachine(IScreenBinder binder)
        {
            _binder = binder;
        }


        public IScreenState CurrentState
        {
            get { return _currentState; }
        }

        public ScreenStateExpression<ENUM> OnStateChangeTo(ENUM stateName)
        {
            return new ScreenStateExpression<ENUM>(this, stateName);
        }

        public IScreenState GetState(ENUM stateName)
        {
            return _states[stateName];
        }

        public void ChangeStateTo(ENUM stateName)
        {
            IScreenState state = GetState(stateName);
            _binder.EnableControls(state);
            _currentState = state;
        }

        public void MarkElementAsReadOnly(Control control)
        {
            IScreenElement element = _binder.FindElementForControl(control);
            element.ActivationMode = ActivationMode.ReadOnly;
            element.EnableControl(CurrentState);
        }

        public void MarkElementForNormalActivation(Control control)
        {
            IScreenElement element = _binder.FindElementForControl(control);
            element.ActivationMode = ActivationMode.Normal;
            element.EnableControl(CurrentState);
        }

        #region Nested type: ScreenStateExpression

        public class ScreenStateExpression<ENUM>
        {
            private readonly ScreenStateMachine<ENUM> _parent;
            private readonly ENUM _stateName;

            public ScreenStateExpression(ScreenStateMachine<ENUM> parent, ENUM stateName)
            {
                _parent = parent;
                _stateName = stateName;
            }

            public void Enable(params object[] controls)
            {
                _parent._all.AddRange(controls);
                var state = new NormalScreenState(_parent._all);
                state.Enable(controls);
                _parent._states.Add(_stateName, state);
            }

            public void DisableAll()
            {
                var state = new DisableAllScreenState();
                _parent._states.Add(_stateName, state);
            }

            public void DisableAllBut(params object[] controls)
            {
                var state = new DisableAllButScreenState();
                state.Enable(controls);
                _parent._states.Add(_stateName, state);
            }

            public void EnableAll()
            {
                var state = new EnableAllScreenState();
                _parent._states.Add(_stateName, state);
            }

            public void EnableAllBut(params object[] controls)
            {
                var state = new EnableAllButScreenState();
                state.Disable(controls);
                _parent._states.Add(_stateName, state);
            }
        }

        #endregion
    }

    public class DisableAllScreenState : IScreenState
    {
        public bool IsControlEnabled(object control)
        {
            return false;
        }
    }

    public class VisibilityStateMachine<ENUM>
    {
        private readonly ControlSet _allControls = new ControlSet();
        private readonly Dictionary<ENUM, VisibleState> _states = new Dictionary<ENUM, VisibleState>();

        public VisibilityStateExpression OnStateChangeTo(ENUM key)
        {
            var expression = new VisibilityStateExpression(_allControls);
            _states.Add(key, expression.State);
            return expression;
        }

        public void ChangeStateTo(ENUM key, IScreenBinder binder)
        {
            _states[key].Process(binder);
        }

        #region Nested type: VisibilityStateExpression

        public class VisibilityStateExpression
        {
            private readonly VisibleState _state;

            internal VisibilityStateExpression(ControlSet allControls)
            {
                _state = new VisibleState(allControls);
            }


            internal VisibleState State
            {
                get { return _state; }
            }

            public VisibilityStateExpression Show(params Control[] controls)
            {
                _state.Show(controls);
                return this;
            }
        }

        #endregion
    }

    public class VisibleState
    {
        private readonly ControlSet _allControls;
        private readonly ControlSet _controls = new ControlSet();


        public VisibleState(ControlSet allControls)
        {
            _allControls = allControls;
        }

        public void Show(params Control[] controls)
        {
            _controls.AddRange(controls);
            _allControls.AddRange(controls);
        }

        public void Process(IScreenBinder binder)
        {
            binder.InsideLatch(() =>
            {
                foreach (Control control in _allControls.AllControls)
                {
                    IScreenElement element = binder.FindElementForControl(control);
                    if (element == null)
                    {
                        control.Hide();
                    }
                    else
                    {
                        element.Hide();
                    }
                }

                foreach (Control control in _controls.AllControls)
                {
                    IScreenElement element = binder.FindElementForControl(control);
                    if (element == null)
                    {
                        control.Show();
                    }
                    else
                    {
                        element.Show();
                    }
                }
            });
        }
    }
}